'Imports System
'Imports System.IO
'Imports System.Net.Http
'Imports System.Text
'Imports System.Threading.Tasks
'Imports Newtonsoft.Json.Linq
'
'
'    Public Class QwenClient
'        Private ReadOnly _apiKey As String
'        Private ReadOnly _model As String ="qwen-plus"' "qwen-max"
'        Private ReadOnly _apiUrl As String = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation"
'       
'        Public Sub New(apiKey As String)
'            If String.IsNullOrWhiteSpace(apiKey) Then
'                Throw New ArgumentException("API Key 不能为空", NameOf(apiKey))
'            End If
'            _apiKey = apiKey
'        End Sub
'
'        Public Async Function GetResponseAsync(prompt As String) As Task(Of String)
'            If String.IsNullOrWhiteSpace(prompt) Then
'                Return "错误：问题不能为空。"
'            End If
'
'            Using client As New HttpClient()
'                client.DefaultRequestHeaders.Add("Authorization", "Bearer " & _apiKey)
'
'                Dim messages As New JArray()
'                messages.Add(New JObject() From {
'                    {"role", "user"},
'                    {"content", prompt}
'                })
'
'                Dim jsonContent As New JObject() From {
'                    {"model", _model},
'                    {"input", New JObject() From {
'                        {"messages", messages}
'                    }},
'                    {"parameters", New JObject() From {
'                        {"result_format", "message"}
'                    }}
'                }
'
'                Dim content As New StringContent(jsonContent.ToString(), Encoding.UTF8, "application/json")
'
'                Try
'                    Dim response As HttpResponseMessage = Await client.PostAsync(_apiUrl, content)
'                    Dim responseString As String = Await response.Content.ReadAsStringAsync()
'
'                    If response.IsSuccessStatusCode Then
'                        Dim jsonResponse As JObject = JObject.Parse(responseString)
'                        Return jsonResponse("output")("choices")(0)("message")("content").ToString()
'                    Else
'                        Return $"HTTP {response.StatusCode}: {responseString}"
'                    End If
'                Catch ex As Exception
'                    Return $"请求失败: {ex.Message}{If(ex.InnerException IsNot Nothing, $" | 内部: {ex.InnerException.Message}", "")}"
'                End Try
'            End Using
'        End Function
'
'        Public Sub SaveConversationToLocal(question As String, answer As String, filePath As String)
'            Try
'                Dim separator As String = New String("-"c, 50)
'                Dim logEntry As String = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 问题: {question}{Environment.NewLine}回答: {answer}{Environment.NewLine}{separator}{Environment.NewLine}"
'                File.AppendAllText(filePath, logEntry)
'            Catch ex As Exception
'                ' 日志保存失败不中断主流程
'            End Try
'        End Sub
'    End Class
' QwenClient.vb
' 通义千问 API 客户端封装类
' 提供异步调用、错误处理、日志记录功能
'
' 设计原理：
'   - 使用 HttpWebRequest 实现 POST 请求
'   - 异步方法避免 UI 冻结
'   - 手动拼接 JSON 提高性能（生产环境建议使用 JObject）
'   - 错误处理包含网络异常和 API 错误
'
' 使用方法：
'   Dim client As New QwenClient("your-api-key")
'   Dim answer As String = Await client.GetResponseAsync("你好")
'
' 注意事项：
'   - API Key 需保密，建议从配置文件读取
'   - 当前使用 qwen-turbo 模型，可替换为 qwen-plus
'   - 简单 JSON 解析可能不稳定，建议使用 Json.NET 解析完整响应
'   - 未实现重试机制，网络差时可能失败

Imports System
Imports System.IO
Imports System.Net
Imports System.Net.Http
Imports System.Text
Imports System.Threading.Tasks
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq

Namespace QwenClientLibrary

    Public Class QwenClient

     
   

        ' 【常量】通义千问 API 端点
        ' 支持模型：qwen-turbo, qwen-plus, qwen-max
        Private Const ApiUrl As String = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation"
        ' ——————————————————————————
        ' 【私有字段】类的全局状态
        ' ——————————————————————————
        Private ReadOnly _apiKey As String      ' API 密钥（必需）   ' 【私有字段】API 密钥，用于身份验证
        Private ReadOnly _model As String       ' 模型名称（可选，默认 qwen-turbo）
        Private ReadOnly _apiUrl As String      ' API 地址（可选，用于未来扩展）

        ' ——————————————————————————
        ' 【常量】默认值定义
        ' ——————————————————————————
        Private Const DefaultModel As String = "qwen-turbo"
        Private Const DefaultApiUrl As String = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation"
        ' 【构造函数】初始化客户端
        ' 参数：
        '   apiKey: 你的 DashScope API Key
        ' 示例：
        '   Dim client As New QwenClient("sk-xxxxxx")
        Public Sub New(apiKey As String)
            If String.IsNullOrWhiteSpace(apiKey) Then
                Throw New ArgumentException("API Key 不能为空")
            End If
            _apiKey = apiKey
        End Sub
''' <summary>
''' 异步获取通义千问（Qwen）AI 模型的文本回复
''' </summary>
''' <param name="prompt">用户输入的提问内容</param>
''' <returns>AI 的文本回复，或格式化的错误信息</returns>
''' 
''' <remarks>
''' 【设计目标】
''' 1. 安全：防止空输入、空密钥、网络异常
''' 2. 可靠：使用现代 HttpClient + JObject 构建请求
''' 3. 可维护：结构清晰，错误信息详细，便于调试
''' 4. 兼容性：适配 DashScope API v1 的文本生成接口
''' 
''' 【API 文档参考】
''' https://help.aliyun.com/zh/dashscope/developer-reference/api-details
''' 
''' 【请求结构说明】
''' POST https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation
''' Headers:
'''   Authorization: Bearer <API-Key>
'''   Content-Type: application/json
''' 
''' Body 示例:
''' {
'''   "model": "qwen-turbo",
'''   "input": {
'''     "messages": [
'''       { "role": "user", "content": "你好" }
'''     ]
'''   },
'''   "parameters": {
'''     "result_format": "message"
'''   }
''' }
''' 
''' 【响应结构示例】
''' {
'''   "output": {
'''     "choices": [
'''       {
'''         "message": {
'''           "role": "assistant",
'''           "content": "你好！我是通义千问。"
'''         }
'''       }
'''     ]
'''   },
'''   "request_id": "..."
''' }
''' 
''' 【错误处理策略】
''' - 空输入 → 返回用户友好提示
''' - 网络异常 → 捕获 HttpRequestException
''' - JSON 解析失败 → 捕获 JsonException，并返回原始响应
''' - API 错误（4xx/5xx）→ 返回状态码 + 原始错误信息
''' </remarks>
Public Async Function GetResponseAsync(prompt As String) As Task(Of String)
    
    ' ——————————————————————————
    ' 步骤 1：输入验证
    ' ——————————————————————————
    If String.IsNullOrWhiteSpace(prompt) Then
        Return "错误：问题不能为空。请输入有效的问题内容。"
    End If

    ' 使用 Using 确保 HttpClient 被正确释放
    ' 注意：在高并发场景下，建议使用 IHttpClientFactory
    Using client As New HttpClient()

        Try
            ' ——————————————————————————
            ' 步骤 2：设置请求头
            ' ——————————————————————————
            ' 使用标准方式设置 Bearer Token
            ' 避免手动拼接字符串，提高安全性与可读性
            client.DefaultRequestHeaders.Authorization =
                New System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _apiKey)

            ' （可选）设置请求标识，便于服务端日志追踪
            ' client.DefaultRequestHeaders.UserAgent.ParseAdd("QwenClient/1.0")

            ' ——————————————————————————
            ' 步骤 3：构建请求体（JSON）
            ' ——————————————————————————
            ' 使用 JArray 和 JObject 构建结构化 JSON
            ' 支持多轮对话扩展（只需在 messages 中添加更多消息）
            Dim messages As New JArray()
            messages.Add(New JObject() From {
                {"role", "user"},
                {"content", prompt}
            })

            ' 构建完整的请求对象
            ' 结构说明：
            '   model: 指定使用的模型（如 qwen-turbo, qwen-plus）
            '   input.messages: 对话历史（当前仅包含用户输入）
            '   parameters.result_format: 返回格式为 "message"（结构化对象）
            Dim jsonContent As New JObject() From {
                {"model", _model}, ' 来自类成员变量，便于统一管理
                {"input", New JObject() From {
                    {"messages", messages}
                }},
                {"parameters", New JObject() From {
                    {"result_format", "message"} ' 推荐使用 message 格式
                }}
            }

            ' 将 JObject 序列化为紧凑 JSON 字符串（无换行缩进）
            ' Formatting.None 可减小请求体积，避免某些服务器拒绝格式化 JSON
            Dim jsonString As String = jsonContent.ToString(Formatting.None)

            ' 创建 HTTP 请求内容
            ' 指定 UTF-8 编码和 application/json 类型
            Dim content As New StringContent(jsonString, Encoding.UTF8, "application/json")

            ' ——————————————————————————
            ' 步骤 4：发送请求并获取响应
            ' ——————————————————————————
            Dim response As HttpResponseMessage = Await client.PostAsync(_apiUrl, content)
            Dim responseString As String = Await response.Content.ReadAsStringAsync()

            ' ——————————————————————————
            ' 步骤 5：处理响应
            ' ——————————————————————————
            If response.IsSuccessStatusCode Then
                ' 成功响应：解析 JSON 并提取 content 字段
                Dim jsonResponse As JObject = JObject.Parse(responseString)

                ' 使用空条件运算符（?）安全访问嵌套属性
                ' 避免 NullReferenceException
                Dim contentToken = jsonResponse("output")?("choices")?(0)?("message")?("content")

                If contentToken IsNot Nothing Then
                    Return contentToken.ToString()
                Else
                    Return "解析错误：API 响应中未找到 'output.choices[0].message.content' 字段。响应内容：" & responseString
                End If

            Else
                ' 失败响应：返回状态码和原始错误信息
                ' 便于用户或开发者定位问题（如 401 密钥错误、400 参数错误）
                Return $"HTTP {response.StatusCode} {response.ReasonPhrase}: {responseString}"
            End If

        Catch webEx As System.Net.Http.HttpRequestException
            ' 网络层异常：如 DNS 失败、连接超时、TLS 握手失败
            Return $"网络请求失败: {webEx.Message}"

        Catch jsonEx As JsonException
            ' JSON 解析异常：响应不是合法 JSON（如服务器返回 HTML 错误页）
            Return $"JSON 解析失败: {jsonEx.Message}。"'原始响应: {responseString}"

        Catch keyEx As ArgumentException When keyEx.ParamName = "apiKey"
            ' API 密钥为空或无效（可在构造函数中抛出）
            Return "配置错误：API 密钥未设置或无效。"

        Catch ex As Exception
            ' 捕获其他未预期异常
            ' 使用 If 函数实现条件拼接，避免空引用
            Dim innerMsg = If(ex.InnerException IsNot Nothing, $" | 内部异常: {ex.InnerException.Message}", "")
            Return $"未知错误: {ex.Message}{innerMsg}"
        End Try

    End Using ' 自动释放 HttpClient 资源

End Function

        ' 【日志方法】保存对话到本地文件（可选）
        ' 参数：
        '   question: 用户问题
        '   answer: AI 回答
        '   filePath: 保存路径（如 "log.txt"）
        ' 用途：
        '  - 调试
        '' - 审计
        '' - 数据备份
        Public Sub SaveConversationToLocal(question As String, answer As String, filePath As String)
            Try
                Dim separator As String = New String("-"c, 50)
                Dim logEntry As String = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 问题: {question}{Environment.NewLine}回答: {answer}{Environment.NewLine}{separator}{Environment.NewLine}"
                File.AppendAllText(filePath, logEntry, Encoding.UTF8)
            Catch ex As Exception
                ' 忽略日志写入错误（如磁盘满）
                Console.WriteLine("日志保存失败: " & ex.Message)
            End Try
        End Sub
    End Class

End Namespace